﻿using System;
using System.Linq;
using Grpc.Core;
using K9Nano.AspNetCore.Api;
using K9Nano.Share.Exceptions;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.Extensions.Logging;

namespace K9Nano.AspNetCore.Filters
{
    public class K9ExceptionFilter : IExceptionFilter
    {
        private readonly ILogger _logger;

        public K9ExceptionFilter(ILogger<K9ExceptionFilter> logger)
        {
            _logger = logger;
        }

        public void OnException(ExceptionContext context)
        {
            string error = "内部错误";
            int code = 500;
            int statusCode = 500;
            string action;

            if (context.ActionDescriptor is ControllerActionDescriptor controller)
            {
                action = $"{controller.ControllerName}.{controller.ActionName}";
            }
            else
            {
                action = context.HttpContext.Request.GetDisplayUrl();
            }

            if (context.Exception is K9UserFriendlyException userFriendlyException)
            {
                code = userFriendlyException.Code;
                error = userFriendlyException.Message;
                statusCode = 400;
            }
            else if (context.Exception is RpcException rpcEx)
            {
                if (rpcEx.StatusCode == StatusCode.InvalidArgument)
                {
                    statusCode = 400;
                    code = 400;
                    error = rpcEx.Status.Detail;
                }
            }
            else if (context.Exception is K9AuthorizationException authorizationException)
            {
                code = 403;
                statusCode = 403;
                error = authorizationException.Message;
            }

            if (code == 500)
            {
                _logger.LogError(context.Exception, action);
            }

            context.ExceptionHandled = true;

            if (context.Filters.OfType<ApiControllerAttribute>().Any())
            {
                context.Result = new JsonResult(new ApiResponse(code, "请求失败", error))
                {
                    StatusCode = statusCode
                };
            }
            else
            {
                var model = new ErrorViewModel
                {
                    Error = error,
                    Path = action,
                    RequestId = context.HttpContext.TraceIdentifier
                };

                var view = new ViewResult
                {
                    ViewName = "Error",
                    StatusCode = statusCode,
                    ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
                    {
                        Model = model
                    }
                };

                context.Result = view;
            }
        }
    }
}